﻿using System;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
using C4F.VistaP2P.Common;

namespace C4F.VistaP2P.Win.Chat
{
    /// <summary>
    /// Chat Class.  
    /// Use this control to enable sending and viewing text messages from others in the mesh
    /// </summary>
    [Description("Use this control with the NetworkManager to share text messages"),
    ToolboxBitmap(typeof(resfinder), "C4F_P2PWinformControls.c4f.bmp"),
     Designer(typeof(ChatDesigner))]
    public partial class ChatControl : UserControl
    {
        /// <summary>
        /// The contstructor for the chat control. We register for the default button click here.
        /// </summary>
        public ChatControl()
        {
            InitializeComponent();
            btnSendMessage.Click += new EventHandler(_SendMessage_Click); //register for the default button click
        }


        /// <summary>
        /// All of the events and methods that the Chat control uses are exposed here so you can register for the
        /// messages here.
        /// </summary>        
        #region public methods and events to use when completely overriding this control

        
        /// <summary>
        /// Once connected to the network this method is used to send a text message.
        /// </summary>
        /// <param name="text">the string to send.</param>
        public void SendText(string text)
        {
            if (SendingText != null)
            {
                SendingText(this, new ChatChangedEventArgs(NewMessage.Text));                
            }
        }

        /// <summary>
        /// This event is raised when a new message is received from the mesh.
        /// </summary>
        public event EventHandler<ChatChangedEventArgs> MessageReceived;
        
        /// <summary>
        /// This event is raised when a node connects or disconnects from the mesh.
        /// The sender object is a string who's value is this node's userName.
        /// </summary>
        public event EventHandler<StatusChangedEventArgs> StatusChanged;

        #endregion

        #region properties

        //The custom messagelog if set.
        private TextBox mMessageLog = null;
        /// <summary>
        /// The MessageBox Textbox property.  Set this to another textbox if you wish to customize the control some.
        /// The Attributes (Description and Category) expose the property on the Winforms design surface.
        /// The get method returns the custom textbox if set (mMessageLog), or the default one (tbMessageLog) if one
        /// is not set.
        /// </summary>
        /// <value></value>
        [
           Description("Specify a textbox to use as the MessageLog textbox, otherwise it will use the default MessageLog textbox"),
           Category("P2P/controls")
        ]
        public TextBox MessageLog
        {
            get
            {
                if (mMessageLog == null)
                    return tbMessageLog;
                else
                    return mMessageLog;
            }
            set
            {
                mMessageLog = value;
                if (value != null)
                {
                    tbMessageLog.Visible = false;
                }
                else
                {
                    this.Show();
                    tbMessageLog.Visible = true;
                }
            }
        }

        //the custom textbox to type new messages into
        private TextBox mNewMessage = null;
        /// <summary>
        /// The NewMessage Textbox property.  Set this to another textbox if you wish to customize the control some.
        /// The Attributes (Description and Category) expose the property on the Winforms design surface.
        /// The get method returns the custom textbox if set (mNewMessage), or the default one (tbNewMessage) if one
        /// is not set.
        /// </summary>
        [
           Description("Specify a textbox to use as the location to type new messages, otherwise it will use the default textbox"),
           Category("P2P/controls")
        ]
        public TextBox NewMessage
        {
            get
            {
                if (mNewMessage == null)
                    return tbNewMessage;
                else
                    return mNewMessage;
            }
            set
            {
                mNewMessage = value;
                if (value != null)
                {                    
                    tbNewMessage.Visible = false;
                }
                else
                {
                    this.Show();
                    tbNewMessage.Visible = true;
                }
            }
        }

        /// <summary>
        /// the custom button to use when sending messages.
        /// </summary>
        private Button mSendMessage = null;
        
        
        [
           Description("Specify a textbox to use as the password textbox, otherwise it will use the networkManager's passwordName textbox"),
           Category("P2P/controls")
        ]
        /// <summary>
        /// The SendMessage Button property.  Set this to another button if you wish to customize the control some.
        /// The Attributes (Description and Category) expose the property on the Winforms design surface.
        /// The get method returns the custom button if set (mSendMessage), or the default one (btnSendMessage) if one
        /// is not set.
        /// </summary>
        public Button SendMessage
        {
            get
            {
                if (mSendMessage == null)
                    return btnSendMessage;
                else
                    return mSendMessage;
            }
            set
            {
                //if we previously registered for a button, unregister for it.
                if (mSendMessage != null)
                {
                    mSendMessage.Click -= new EventHandler(_SendMessage_Click);
                }

                if (value != null)
                {
                    //hide the default control's button and unregister for the event
                    btnSendMessage.Visible = false;
                    btnSendMessage.Click -= new EventHandler(_SendMessage_Click);

                    //setup the new control and register for the new event. 
                    mSendMessage = value;
                    mSendMessage.Click += new EventHandler(_SendMessage_Click);

                }
                else
                {
                    this.Show();
                    btnSendMessage.Visible = true;
                    btnSendMessage.Click += new EventHandler(_SendMessage_Click);
                }
            }
        }

#endregion

        /// <summary>
        /// Have to shadow the visible property for smart tag on the design surface..
        /// </summary>
        internal bool ShadowVisible
        {
            get
            {
                return Visible;
            }
            set
            {
                Visible = value;
            }
        }

       

        /// <summary>
        /// The event that the NetworkManager subscribes to so it can send text messages out to the mesh.
        /// </summary>
        internal event EventHandler<ChatChangedEventArgs> SendingText;

        /// <summary>
        /// Handles the send message button click, sends the data in the textbox and clears the text in the NewMessage box.
        /// </summary>
        /// <param name="sender">unused</param>
        /// <param name="e">unused</param>
        private void _SendMessage_Click(object sender, EventArgs e)
        {
            SendText(NewMessage.Text);
            NewMessage.Clear(); //mesage sent clear the textbox.
        }

        
        /// <summary>
        /// look for a carriage return "\r\n" so we can send out the text message when the user hits enter in the textbox.
        /// </summary>
        /// <param name="sender">unused</param>
        /// <param name="e">unused</param>
        private void _tbNewMessage_TextChanged(object sender, EventArgs e)
        {
            TextBox tb = (TextBox)sender;
            if(tb.Text.Contains("\r\n"))
            {
                SendMessage.PerformClick();
            }
        }

        /// <summary>
        /// _StatusChangedHandler delegate used to ensure that the control is only changed by the thread that created it.
        /// look for crossthreading issues for more information.
        /// </summary>
        /// <param name="userName">The username of the NetworkManager tha this Chat control is connected to</param>
        /// <param name="e">The Status changed Event</param>
        private delegate void _SetStatusChangedHandler(string userName, StatusChangedEventArgs e);
        /// <summary>
        /// called by the network manager to change the status of this control so it knows if it is online or not.
        /// </summary>
        /// <param name="userName">The username that the network manager has saved for this node.</param>
        /// <param name="e">used to determine if the node is joining or leaving</param>
        internal void SetStatusChanged(string userName, C4F.VistaP2P.Common.StatusChangedEventArgs e)
        {
            if (NewMessage.InvokeRequired)
            {
                //invoke the call on the controls thread.
                NewMessage.Invoke(new _SetStatusChangedHandler(SetStatusChanged), new object[] { userName, e });
            }
            else
            {
                //we're on the controls thread...change it if necessary
                if (userName == e.Member && e.NewNodeJoined)
                {
                    SendMessage.Enabled = true;
                    NewMessage.Enabled = true;
                }

                if (userName == e.Member && e.NodeLeft)
                {
                    SendMessage.Enabled = false;
                    NewMessage.Enabled = false;
                }

                //raise the status changed event for anyone listening.
                if (null != StatusChanged)
                {
                    StatusChanged(userName, e);
                }
            }
        }

        /// <summary>
        /// _SetMessageReceivedHandler delegate used to ensure that the control is only changed by the thread that created it.
        /// look for crossthreading issues for more information.
        /// </summary>        
        /// <param name="chatChanged">The Chat changed Event that contains the message</param>
        private delegate void _SetMessageReceivedHandler(ChatChangedEventArgs chatChanged);

        /// <summary>
        /// Called by the NetworkManager to let this control know that a message was received.
        /// </summary>
        /// <param name="chatChanged">The chat message</param>
        internal void SetMessageReceived(ChatChangedEventArgs chatChanged)
        {
            if (MessageLog.InvokeRequired)
            {
                // Invocation required 
                MessageLog.Invoke(new _SetMessageReceivedHandler(SetMessageReceived), chatChanged);                
            }
            else
            {
                if (null != MessageReceived)
                {
                    MessageReceived(this, chatChanged);
                }

                MessageLog.Text += chatChanged.Message + "\r\n";
                MessageLog.SelectAll();
                MessageLog.ScrollToCaret();
            }
        }

        /// <summary>
        /// _SetConnectionStatusHandler delegate used to ensure that the control is only changed by the thread that created it.
        /// look for crossthreading issues for more information.
        /// </summary>        
        /// <param name="state">the state of the system, to ensure the button status is appropriate.</param>
        private delegate void _SetConnectionStatusHandler(SystemState state);
        internal void SetConnectionStatus(SystemState state)
        {
            if (SendMessage.InvokeRequired)
            {
                SendMessage.Invoke(new _SetConnectionStatusHandler(SetConnectionStatus), state);
            }
            else
            {
                if (state == SystemState.LoggedIn)
                {
                    SendMessage.Enabled = true;
                    NewMessage.Enabled = true;
                }
                else if (state == SystemState.LoggedOut)
                {
                    SendMessage.Enabled = false;
                    NewMessage.Enabled = false;
                }
            }
        }
    }
}
